package com.time.admin.domain.service.impl;

import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.time.admin.common.entity.PageResult;
import com.time.admin.common.enums.IsDeletedFlagEnum;
import com.time.admin.domain.constants.ExceptionCode;
import com.time.admin.domain.converter.TagBOConverter;
import com.time.admin.domain.entity.TagBO;
import com.time.admin.domain.exception.AlreadyExistsException;
import com.time.admin.domain.redis.RedisUtil;
import com.time.admin.domain.service.AdminTagDomainService;
import com.time.admin.infra.basic.entity.Tag;
import com.time.admin.infra.basic.service.PostTagService;
import com.time.admin.infra.basic.service.TagService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
import java.util.Objects;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.TimeUnit;

/**
 * 标签管理服务层接口实现类
 *
 * @author: HuangQi
 * @date: Created in 11:13 2025/10/14
 * @description: 标签管理服务层接口实现类
 */
@Slf4j
@Service("adminTagDomainServiceImpl")
public class AdminTagDomainServiceImpl implements AdminTagDomainService {

    @Autowired
    private TagService tagService;

    @Autowired
    private PostTagService postTagService;

    @Autowired
    private RedisUtil redisUtil;

    private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
    private static final long TAG_CACHE_TIMEOUT_MINUTES = 10L;
    private static final long TAG_CACHE_JITTER_MINUTES = 5L;
    private static final TimeUnit TAG_CACHE_TIMEUNIT = TimeUnit.MINUTES;

    @Override
    public Boolean add(TagBO tagBO) {
        if (log.isInfoEnabled()) {
            log.info("AdminTagDomainServiceImpl.add.bo:{}", tagBO);
        }

        // 参数转换
        Tag tag = TagBOConverter.INSTANCE.convertBOToEntity(tagBO);
        tag.setIsDeleted(IsDeletedFlagEnum.UN_DELETED.getCode());

        // 检查当前标签的名称是否已经存在
        if (isExistTag(tag.getTagName())) {
            throw new AlreadyExistsException(ExceptionCode.TAG_ALREADY_EXISTS, "该标签名称已经存在");
        }

        // 调用 service
        int count = tagService.insert(tag);

        return count > 0;
    }

    /**
     * 检查标签名称是否已经存在
     *
     * @param tagName 标签名称
     * @return true表示存在，false表示不存在
     */
    private boolean isExistTag(String tagName) {
        Tag tag = tagService.queryByTagName(tagName);
        return !Objects.isNull(tag);
    }

    @Override
    public Boolean update(TagBO tagBO) {
        if (log.isInfoEnabled()) {
            log.info("AdminTagDomainServiceImpl.update.bo:{}", tagBO);
        }

        // 参数转换
        Tag tag = TagBOConverter.INSTANCE.convertBOToEntity(tagBO);

        // 调用 service
        int count = tagService.update(tag);

        return count > 0;
    }

    @Override
    @Transactional
    public Boolean delete(Long id) {
        if (log.isInfoEnabled()) {
            log.info("AdminTagDomainServiceImpl.delete.id:{}", id);
        }

        // 删除标签信息
        Tag tag = new Tag();
        tag.setTagId(id);
        tag.setIsDeleted(IsDeletedFlagEnum.DELETED.getCode());

        // 删除标签和文章的关联关系
        postTagService.deleteByTagId(id);

        // 调用 service
        int count = tagService.update(tag);

        return count > 0;
    }

    @Override
    public PageResult<TagBO> queryAllTag(TagBO tagBO) {
        if (log.isInfoEnabled()) {
            log.info("AdminTagDomainServiceImpl.queryAllTag.bo:{}", tagBO);
        }

        String cacheKey = redisUtil.buildKey("admin", "tag", "queryAll", serializeKey(tagBO));
        PageResult<TagBO> cacheResult = getCache(cacheKey, new TypeReference<PageResult<TagBO>>() {});
        if (cacheResult != null) {
            return cacheResult;
        }

        // 参数转换
        Tag tag = TagBOConverter.INSTANCE.convertBOToEntity(tagBO);
        tag.setIsDeleted(IsDeletedFlagEnum.UN_DELETED.getCode());

        // 创建分页结果
        PageResult<TagBO> pageResult = new PageResult<>();
        pageResult.setPageNo(tagBO.getPageNo());
        pageResult.setPageSize(tagBO.getPageSize());

        // 获取总记录数
        int total = tagService.countAll();

        // 获取分页的标签信息
        int start = (pageResult.getPageNo() - 1) * pageResult.getPageSize();
        List<Tag> tagList = tagService.queryPage(tag, start, pageResult.getPageSize());

        // 参数转换
        List<TagBO> tagBOList = TagBOConverter.INSTANCE.convertEntityListToBO(tagList);
        // 获取每个分类关联的文章数量
        tagBOList.forEach(item -> {
            int postCount = postTagService.queryPostCountByTagId(item.getTagId());
            item.setPostCount(postCount);
        });

        // 构建分页结果
        pageResult.setTotal(total);
        pageResult.setResult(tagBOList);

        setCache(cacheKey, pageResult);
        // 返回结果
        return pageResult;
    }

    /**
     * 从 Redis 读取缓存
     */
    private <T> T getCache(String key, TypeReference<T> typeReference) {
        try {
            String cacheValue = redisUtil.get(key);
            if (cacheValue == null) {
                return null;
            }
            return OBJECT_MAPPER.readValue(cacheValue,
                    OBJECT_MAPPER.getTypeFactory().constructType(typeReference.getType()));
        } catch (Exception e) {
            log.warn("AdminTagDomainServiceImpl.getCache.error:key={}, msg={}", key, e.getMessage());
            return null;
        }
    }

    /**
     * 写入 Redis 缓存，带轻微抖动避免雪崩
     */
    private void setCache(String key, Object value) {
        try {
            long ttl = TAG_CACHE_TIMEOUT_MINUTES +
                    ThreadLocalRandom.current().nextLong(TAG_CACHE_JITTER_MINUTES + 1);
            redisUtil.set(key, OBJECT_MAPPER.writeValueAsString(value), ttl, TAG_CACHE_TIMEUNIT);
        } catch (Exception e) {
            log.warn("AdminTagDomainServiceImpl.setCache.error:key={}, msg={}", key, e.getMessage());
        }
    }

    /**
     * 将查询条件序列化为缓存 key 片段
     */
    private String serializeKey(Object obj) {
        try {
            return OBJECT_MAPPER.writeValueAsString(obj);
        } catch (Exception e) {
            log.warn("AdminTagDomainServiceImpl.serializeKey.error:{}", e.getMessage());
            return String.valueOf(obj);
        }
    }
}
